﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace TcpServerDemo
{
    public class SocketClientState
    {
        public Socket Socket { get; set; }
        public NetworkStream Stream { get; set; }
        public byte[] Buffer { get; set; }
    }
    public partial class Program
    {
        static List<SocketClientState> socketClients = [];

        static void Main(string[] args)
        {
            var encoding = Encoding.UTF8;
            var address = IPAddress.Any;
            var port = 8181;


            var socket = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.IP);
            socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
            //var stream = new NetworkStream(socket);
            SetKeepAlive(socket, 60000, 10000);

            var ipLocal = new IPEndPoint(address, port);
            socket.Bind(ipLocal);
            socket.Listen(100);

            socket.BeginAccept(new AsyncCallback(SocketAccepted), socket);

            Console.ReadLine();
        }

        static void SetKeepAlive(Socket socket, UInt32 keepAliveInterval, UInt32 retryInterval)
        {
            int size = sizeof(UInt32);
            UInt32 on = 1;

            byte[] inArray = new byte[size * 3];
            Array.Copy(BitConverter.GetBytes(on), 0, inArray, 0, size);
            Array.Copy(BitConverter.GetBytes(keepAliveInterval), 0, inArray, size, size);
            Array.Copy(BitConverter.GetBytes(retryInterval), 0, inArray, size * 2, size);
            socket.IOControl(IOControlCode.KeepAliveValues, inArray, null);
        }
        static void SocketAccepted(IAsyncResult ar)
        {
            Socket server = (Socket)ar.AsyncState;
            Socket client = server.EndAccept(ar);
            var internalClient = new SocketClientState()
            {
                Socket = client,
                Stream = new NetworkStream(client),
                Buffer = new byte[client.ReceiveBufferSize],
            };
            lock (socketClients)
            {
                socketClients.Add(internalClient);
                // RaiseClientConnected(tcpClient);
            }

            internalClient.Stream.BeginRead(
                internalClient.Buffer,
                0,
                internalClient.Buffer.Length,
                SocketReceived,
                internalClient);

            server.BeginAccept(new AsyncCallback(SocketAccepted), server);
        }

        static void SocketReceived(IAsyncResult ar)
        {
            var internalClient = (SocketClientState)ar.AsyncState;
            var networkStream = internalClient.Stream;
            int numberOfReadBytes = 0;
            try
            {
                numberOfReadBytes = networkStream.EndRead(ar);
            }
            catch
            {
                numberOfReadBytes = 0;
            }

            if (numberOfReadBytes == 0)
            {
                // connection has been closed
                lock (socketClients)
                {
                    socketClients.Remove(internalClient);
                    return;
                }
            }

            // received byte and trigger event notification
            byte[] receivedBytes = new byte[numberOfReadBytes];
            Buffer.BlockCopy(
                internalClient.Buffer, 0,
                receivedBytes, 0, numberOfReadBytes);

            string msg = Encoding.UTF8.GetString(receivedBytes, 0, receivedBytes.Length);
            Console.WriteLine("接收到消息：{0}", msg);
            GenericSocketReply(internalClient, "Server has received you text: " + msg);

            // continue listening for tcp datagram packets
            networkStream.BeginRead(
                internalClient.Buffer, 0, internalClient.Buffer.Length,
                SocketReceived, internalClient);
        }
        static void GenericSocketReply(SocketClientState client, string data)
        {
            var bytes = Encoding.UTF8.GetBytes(data);

            client.Stream.BeginWrite(
                bytes, 0, bytes.Length, SocketWritten, client);
        }
        static void SocketWritten(IAsyncResult ar)
        {
            SocketClientState internalClient = (SocketClientState)ar.AsyncState;
            internalClient.Stream.EndWrite(ar);
        }

    }
}
